package com.jxpanda.infrastructure.core.tollkit.json;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.json.JsonSanitizer;
import io.github.haibiiin.json.repair.JSONRepair;
import io.github.haibiiin.json.repair.JSONRepairConfig;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Supplier;

@Slf4j
@Getter
@SuppressWarnings("UnusedReturnValue")
public class JsonRepair {

    // 原子引用，初始为 null
    private static final AtomicReference<ObjectMapper> INSTANCE = new AtomicReference<>();

    // 第0步：不做处理先试；后面依次：轻清洗 → Sanitizer → json-repair
    private static final List<Consumer<JsonRepair>> REPAIR_STEPS = List.of(
            // 先尝试直接解析
            jsonRepair -> {
            },
            // 轻清洗（去 Markdown / 尾逗号等）
            JsonRepair::repair,
            // Google JsonSanitizer 兜底
            JsonRepair::sanitize,
            // json-repair 激进修复
            JsonRepair::radicalRepair
    );

    /**
     * (?s) 让 . 匹配换行
     * ``` 或 ```json，然后可能有空格或换行，再把中间所有内容捕获，直到下一个 ```
     */
    private static final String MARKDOWN_JSON_PATTERN = "(?s)```(?:json)?\\s*\\n([\\s\\S]*?)```";

    // 正则：逗号后面紧跟着右大括号或右中括号
    private static final String TRAILING_COMMA_PATTERN = ",(?=\\s*[}\\]])";

    private final String originalJson;

    private String repairedJson;

    public JsonRepair(String originalJson) {
        this.originalJson = originalJson;
        this.repairedJson = originalJson;
    }


    /**
     * 把json字符串转为对象
     */
    public <T> T convert(Class<T> clazz) {
        return convert(() -> {
            try {
                return getInstance().readValue(repairedJson, clazz);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 把json字符串转为对象
     * 取值跟着泛型走，这个可以传递List进来
     */
    public <T> T convert(TypeReference<T> typeReference) {
        return convert(() -> {
            try {
                return getInstance().readValue(repairedJson, typeReference);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        });
    }

    private <T> T convert(Supplier<T> supplier) {
        Exception lastException = null;

        for (Consumer<JsonRepair> repairStep : REPAIR_STEPS) {
            try {
                repairStep.accept(this); // 应用本步修复（或无操作）
                return supplier.get();            // 解析成功则直接返回
            } catch (Exception e) {
                // 仅记录最后一次异常，不在这里打日志
                lastException = e;
            }
        }

        // 全部步骤都失败，仅此处输出一条错误日志
        log.error("[JSON REPAIR FAIL] originalJson={} repairedJson={}",
                originalJson, originalJson, lastException);

        // 不要返回 null，抛出异常让上层感知
        throw (lastException != null) ? (RuntimeException) lastException : new RuntimeException("[JSON REPAIR FAIL] NO EXCEPTION");
    }

    public JsonRepair replaceMarkdown() {
        this.repairedJson = repairedJson.replaceAll(MARKDOWN_JSON_PATTERN, "$1");
        return this;
    }

    /**
     * 去除 JSON 字符串中对象或数组结尾处多余的逗号。
     *
     * @return 已去除尾逗号后的 JSON 文本
     */
    public JsonRepair removeTrailingCommas() {
        this.repairedJson = repairedJson.replaceAll(TRAILING_COMMA_PATTERN, "");
        return this;
    }

    public JsonRepair repair() {
        replaceMarkdown();
        removeTrailingCommas();
        return this;
    }

    /**
     * 使用 Google JsonSanitizer 做一次语法兜底修复。
     * 要求：工程中已引入 com.google.json:json-sanitizer 依赖。
     */
    public JsonRepair sanitize() {
        if (repairedJson != null) {
            repairedJson = JsonSanitizer.sanitize(repairedJson);
        }
        return this;
    }

    /**
     * 使用 json-repair 做一次激进修复。
     */
    public JsonRepair radicalRepair() {
        if (repairedJson != null) {
            JSONRepairConfig repairConfig = new JSONRepairConfig();
            repairConfig.enableExtractJSON();
            repairedJson = new JSONRepair(repairConfig).handle(repairedJson);

        }
        return this;
    }

    private static ObjectMapper getInstance() {
        return INSTANCE.updateAndGet(existing -> existing != null ? existing : JacksonBuilder.lenientMapper());
    }

}
